package TDAMapeo;

import Excepciones.BoundaryViolationException;
import Excepciones.InvalidKeyException;
import Excepciones.InvalidPositionException;
import TDALista.DoubleLinkedList;
import TDALista.Position;
import TDALista.PositionList;




public class Mapeo<K, V> implements Map<K, V> {
	
	 private PositionList<Entrada<K,V>> map;
	 
	 public Mapeo (){
		 
		 map = new DoubleLinkedList<Entrada<K,V>>();
		 
	 }
	 

	@Override
	public int size() {
		return map.size();
	}

	@Override
	public boolean isEmpty() {
		return size()==0;
	}

	@Override
	public V get(K k) throws InvalidKeyException{
		
		K key = checkKey(k);
		
		Entrada<K,V> ent = encontrar (key);
		if (ent != null)
			return ent.getValue();
		else
			return null;
	
	}
	
	private Entrada<K,V> encontrar (K k){
		Position<Entrada<K,V>> cursor = map.first();
		boolean encontre = false;
		
		while (!encontre && cursor != map.last()){
			if (cursor.element().getKey().equals (k))
				encontre = true;
			else
				try{
					
					cursor = map.next(cursor);
				}catch (InvalidPositionException e){e.printStackTrace();}
				catch (BoundaryViolationException e){e.printStackTrace();}
		}
		
		if (cursor.element().getKey().equals(k)){
			return cursor.element();
		}
		else
			return null;
		
	}

	private K checkKey(K k) throws InvalidKeyException {
		if (k == null)
			throw new InvalidKeyException ("Clave invalida");
		
		K key = null;
		
		try {
			key = (K) k;
		}catch (ClassCastException e){ throw new InvalidKeyException ("Clave Invalida"); }
		return key;
	}


	@Override
	public V put(K k, V value)  throws InvalidKeyException {
		K key = checkKey (k);
		if (isEmpty()){
			map.addLast(new Entrada<K,V> (key,value));
			
			return null;
		}
		else{
			Entrada<K,V> ent = encontrar (key);
			if (ent != null){
				V valor = ent.getValue();
				ent.setValue(value);
				return valor;
			}
			else{
				ent = new Entrada<K,V>(key,value);
				map.addLast(ent);
				return null;
			}
		}
	}

	@Override
	public V remove(K k)   throws InvalidKeyException{
		K key = checkKey(k);
		Position<Entrada<K,V>> cursor = map.first();
		boolean encontre = false;
		
		while (!encontre && cursor != map.last()){
			if (cursor.element().getKey().equals (key))
				encontre = true;
			else
				try{
					
					cursor = map.next(cursor);
				}catch (InvalidPositionException e){e.printStackTrace();}
				catch (BoundaryViolationException e){e.printStackTrace();}
		}if (cursor.element().getKey().equals(key)){
			
			V valor = cursor.element().getValue();
			
			try {
			
				map.remove(cursor);
			
			} catch (InvalidPositionException e) {e.printStackTrace();			}
			
			return valor;
		
		}
		else
			return null;
	
	}
	@Override
	public Iterable<K> keys() {
		PositionList<K> lista = new DoubleLinkedList<K>();
		
		for (Entrada<K,V> e: map)
			lista.addLast(e.getKey());
		
		return lista;
	}

	@Override
	public Iterable<V> values() {
		PositionList<V> lista = new DoubleLinkedList<V>();
		
		for (Entrada<K,V> e: map)
			lista.addLast(e.getValue());
		
		return lista;
	}

	@Override
	public Iterable<Entry<K, V>> entries() {
			PositionList<Entry<K,V>> lista = new DoubleLinkedList<Entry<K,V>>();
		
		for (Entrada<K,V> e: map)
			lista.addLast(e);
		return lista;
	}

}
